﻿using System;
using System.Reflection;

namespace Affinity.Flux.Pairings
{
    /// <summary>
    /// A pairing between two properties.
    /// </summary>
    public class PropertyPropertyPairing : Pairing
    {
        /// <summary>
        /// Constructor accepting the property inlet and property outlet being paired.
        /// </summary>
        /// <param name="inlet"></param>
        /// <param name="outlet"></param>
        public PropertyPropertyPairing(Outlet outlet, Inlet inlet)
            : base(outlet, inlet)
        {
            if (inlet == null) throw new ArgumentNullException("inlet");
            if (outlet == null) throw new ArgumentNullException("outlet");
            if (!(inlet.Member is PropertyInfo)) throw new ArgumentException("inlet member must be a PropertyInfo.");
            if (!(outlet.Member is PropertyInfo)) throw new ArgumentException("outlet member must be a PropertyInfo.");

            //Debug.WriteLine("[{0}] {1} Pairing", Contract, typeof(PropertyPropertyPairing).Name);

            outlet.PropertyChanged += HandleOutletPropertyChanged;
            PushValue(outlet.GetPropertyValue());
        }

        /// <summary>
        /// Raised when a value is pushed from the outlet to the inlet.
        /// </summary>
        public event Action<PropertyPropertyPairing, object, TimeSpan> ValuePushed;

        /// <summary>
        /// Raises the ValuePushed event.
        /// </summary>
        private void RaiseValuePushed(object newValue, TimeSpan duration)
        {
            var valuePushed = ValuePushed;
            if (valuePushed != null) valuePushed(this, newValue, duration);
        }

        /// <summary>
        /// Disposes this object.
        /// </summary>
        public override void Dispose()
        {
            //Debug.WriteLine("[{0}] {1} Disposing", Contract, typeof(PropertyPropertyPairing).Name);
            Outlet.PropertyChanged -= HandleOutletPropertyChanged;
        }

        /// <summary>
        /// Handles the PropertyChanged event on the outlet.
        /// </summary>
        /// <param name="newValue"></param>
        private void HandleOutletPropertyChanged(object newValue)
        {
            PushValue(newValue);
        }

        /// <summary>
        /// Pushes the specified value to the inlet.
        /// </summary>
        /// <param name="newValue"></param>
        private void PushValue(object newValue)
        {
            DateTime beginTime = DateTime.Now;
            Inlet.SetPropertyValue(newValue);
            RaiseValuePushed(newValue, DateTime.Now - beginTime);
        }
    }
}
